package demo.java.sql.service;

import demo.java.sql.JdbcDemo;
import demo.util.DesSecurityUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Clock;
import java.util.List;
import java.util.stream.IntStream;

/**
 * 对如下表的部分字段加密
 * <li>1.approve_extend_info（加密卡号）
 * <li>2.approve_info （加密id_no,name）
 * <li>3.approve_info_history（加密id_no,name）
 * <li>4.call_log(只加密用户名字,update_at字段会自动更新，需要避免这个因素发生)
 * 
 * @author hanjy
 *
 */
@Service
public class TableEncryptService {

    private static Logger logger = LoggerFactory.getLogger(TableEncryptService.class);

    @Autowired
    private DataSource maDataSource;

    static Clock clock = Clock.systemDefaultZone();

    /**
     * 对需要加密字段的进行更新
     * 
     * @param select
     * @param update
     * @param rows
     * @param needEncryptIdx
     */
    public void encryptTableData(String select, String update, final int rows, final int[] needEncryptIdx) {
        try (Connection connection = maDataSource.getConnection();
                PreparedStatement srcStatement = connection.prepareStatement(select);) {
            String sqlLog = select.replaceAll("\\?", "{}");
            int offset = 0;
            List<Object[]> list = null;
            do {
                srcStatement.setInt(1, offset);
                srcStatement.setInt(2, rows);
                logger.info(sqlLog, offset, rows);
                long start = clock.millis();
                ResultSet srcRS = srcStatement.executeQuery();
                long end1 = clock.millis();
                logger.info("耗时:{} ms", (end1 - start));
                list = JdbcDemo.buildResultSet(srcRS);

                srcRS.close();

                DesSecurityUtil.encryptOrDecrypt(list, needEncryptIdx, true);

                offset += rows;
                batchUpdate(update, list);
            } while (list.size() == rows);
        } catch (SQLException e) {
            logger.error("", e);
        }
    }

    public int batchUpdate(String updateSql, List<Object[]> list) {
        int counter = 0;
        long start = clock.millis();
        try (Connection connection = maDataSource.getConnection();
                PreparedStatement statement = connection.prepareStatement(updateSql);) {
            connection.setAutoCommit(false);
            for (Object[] array : list) {
                for (int i = 0; i < array.length; i++) {
                    statement.setObject(i + 1, array[i]);
                }
                statement.addBatch();
                if (counter % 5000 == 0) {
                    counter += IntStream.of(statement.executeBatch()).sum();
                    connection.commit();
                }
            }
            counter += IntStream.of(statement.executeBatch()).sum();
            connection.commit();
        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        }
        logger.info("batchUpdate {} records return {}, take {} ms.", list.size(), counter, clock.millis() - start);
        return counter;
    }

}
